# @Version        : 1.0
# @Update Time    : 2025/8/29 0:39
# @File           : dao.py
# @IDE            : PyCharm
# @Desc           : 文件描述信息

from typing import Optional, List, TypeVar

from redis.asyncio import Redis

from ..base import AuthDaoByObjectFollowString
from ...models.session import TerminalInfoModel, AuthSessionModel

# 定义类型变量
T = TypeVar("T")


# --------------------- 会话模型兼容性工具 ---------------------


class RedisAuthDao(AuthDaoByObjectFollowString):
    """基于Redis的实现，适合ObjectFollowString模式（异步版本）"""

    def __init__(self, redis_client: Redis = None, **redis_config):
        """初始化 Redis 连接"""
        self._redis_config = {
            "host": "localhost",
            "port": 6379,
            "db": 5,
            "decode_responses": True,
            "socket_connect_timeout": 5,
            "socket_timeout": 5,
        }
        self._redis_config.update(redis_config)
        self._redis = redis_client

    async def _ensure_connected(self):
        """确保Redis连接已建立"""
        if self._redis is None:
            self._redis = Redis(**self._redis_config)
            try:
                await self._redis.ping()
            except Exception as e:
                raise Exception(f"Redis 连接失败: {e}")

    # 实现String操作相关方法
    async def get(self, key: str) -> Optional[str]:
        await self._ensure_connected()
        return await self._redis.get(key)

    async def set(self, key: str, value: str, timeout: int = -1):
        await self._ensure_connected()
        if timeout == self.NEVER_EXPIRE:
            await self._redis.set(key, value)
        elif timeout > 0:
            await self._redis.setex(key, timeout, value)
        else:
            # 不存储或无效超时时间
            if timeout != 0:  # 0表示不存储
                await self.delete(key)

    async def update(self, key: str, value: str):
        await self._ensure_connected()
        # 获取当前超时时间
        ttl = await self._redis.ttl(key)
        if ttl == self.NOT_VALUE_EXPIRE:  # 键不存在
            return

        if ttl == self.NEVER_EXPIRE:  # 永不过期
            await self._redis.set(key, value)
        else:
            await self._redis.setex(key, ttl, value)

    async def delete(self, key: str):
        await self._ensure_connected()
        await self._redis.delete(key)

    async def get_timeout(self, key: str) -> int:
        await self._ensure_connected()
        ttl = await self._redis.ttl(key)
        if ttl == -2:  # 键不存在
            return self.NOT_VALUE_EXPIRE
        elif ttl == -1:  # 永不过期
            return self.NEVER_EXPIRE
        else:
            return ttl

    async def update_timeout(self, key: str, timeout: int):
        await self._ensure_connected()
        if timeout == self.NEVER_EXPIRE:
            await self._redis.persist(key)
        elif timeout > 0:
            await self._redis.expire(key, timeout)
        else:
            await self.delete(key)

    async def search_data(
        self, prefix: str, keyword: str, start: int, size: int, sort_type: bool
    ) -> List[str]:
        await self._ensure_connected()
        # Redis中使用SCAN命令进行模式匹配
        pattern = f"{prefix}*"
        cursor = 0
        results = []

        try:
            while True:
                cursor, keys = await self._redis.scan(cursor, match=pattern, count=100)
                # 手动过滤包含keyword的键
                for key in keys:
                    if keyword in key:
                        results.append(key)
                if cursor == 0 or len(results) >= start + size > 0:
                    break
        except Exception as e:
            print(f"Redis搜索操作错误: {e}")

        # 排序
        results.sort(reverse=not sort_type)

        # 分页
        if size == -1:
            return results[start:]
        else:
            return results[start : start + size]

    async def close(self):
        """关闭Redis连接"""
        if self._redis is not None:
            await self._redis.close()


def convert_to_pydantic_session(old_session) -> AuthSessionModel:
    """将旧版会话对象转换为pydantic版本的会话对象"""
    if isinstance(old_session, AuthSessionModel):
        return old_session

    # 创建新的pydantic会话对象
    session_data = {}

    # 复制基本属性
    if hasattr(old_session, "session_id"):
        session_data["session_id"] = old_session.session_id
    if hasattr(old_session, "session_type"):
        session_data["session_type"] = old_session.session_type
    if hasattr(old_session, "login_type"):
        session_data["login_type"] = old_session.login_type
    if hasattr(old_session, "login_id"):
        session_data["login_id"] = old_session.login_id
    if hasattr(old_session, "token"):
        session_data["token"] = old_session.token
    if hasattr(old_session, "history_terminal_count"):
        session_data["history_terminal_count"] = old_session.history_terminal_count
    if hasattr(old_session, "create_time"):
        session_data["create_time"] = old_session.create_time

    # 处理data_map
    if hasattr(old_session, "data_map"):
        session_data["data_map"] = old_session.data_map
    elif hasattr(old_session, "get_data_map"):
        session_data["data_map"] = old_session.get_data_map()

    # 处理terminal_list
    terminal_list = []
    if hasattr(old_session, "terminal_list"):
        for terminal in old_session.terminal_list:
            if isinstance(terminal, dict):
                terminal_list.append(terminal)
            elif hasattr(terminal, "to_dict"):
                terminal_list.append(terminal.to_dict())
            else:
                terminal_list.append(terminal.__dict__)
    elif hasattr(old_session, "get_terminal_list"):
        for terminal in old_session.get_terminal_list():
            if isinstance(terminal, dict):
                terminal_list.append(terminal)
            elif hasattr(terminal, "to_dict"):
                terminal_list.append(terminal.to_dict())
            else:
                terminal_list.append(terminal.__dict__)

    session_data["terminal_list"] = terminal_list

    # 创建并返回pydantic会话对象
    return AuthSessionModel(**session_data)


def convert_to_terminal_info_model(terminal) -> TerminalInfoModel:
    """将终端信息转换为pydantic版本"""
    if isinstance(terminal, TerminalInfoModel):
        return terminal

    # 从字典或对象创建
    if isinstance(terminal, dict):
        return TerminalInfoModel(**terminal)
    elif hasattr(terminal, "to_dict"):
        return TerminalInfoModel(**terminal.to_dict())
    else:
        return TerminalInfoModel(**terminal.__dict__)
